Hey there.
Welcome back.
In this lecture, I would like to show you a neat module script that was created by another developer
that allows us to easily manage and clean up instances and connections in our objects created by classes.
When we create an object from a class, sometimes we will no longer need that object when, for example,
the main part that object is associated with is destroyed.
You can think of a class for tools where once the tool instance itself is destroyed, we would also
like to destroy the object associated with that instance.
This is where we would typically have a destroy function in our class to handle that cleanup.
However, it can be a little annoying when we are listening to a bunch of different events and have
several other instances, and then we have to manually disconnect those events and have to destroy those
other instances inside of our destroy function.
There may be times when we sometimes forget to disconnect an event, or we forget to destroy an instance,
and that can potentially lead to some memory leak problems.
For most instances in our game, when we have functions connected to their events and when those objects
are destroyed, those connections are automatically cleaned up.
But this is not the case for all events.
For example, any connections to events and services have to be manually disconnected, such as the
user input service and the run service, and so on.
Any objects we create and parent somewhere in our game also have to be manually destroyed.
So the main purpose of this module, which is called trove, is to help us track any sort of object
during the runtime of our scripts that has to be cleaned up at some point in time.
This helps us to avoid the worry of causing a memory leak in our code, because it's all handled by
this module script.
This module script can be a lifesaver down the road to prevent the problem.
Where you encounter a memory leak that is ruining your game, and you're having trouble finding the
source of that memory leak.
The link to the module, along with the documentation has been attached to this lecture, so you can
go ahead and grab it.
And you can also follow along to see how this module works.
First, let's go ahead and take a look at this example tool class I've created.
Inside of this simple class, we have our table that is representing the class, and then we have the
meta table to attach to all our objects.
We have our constructor here, and the constructor takes a tool.
It associates that tool with our object itself.
It creates some events, some other instances, sets them somewhere in the workspace.
And we also listen to some other events here.
And then eventually when the tool itself or the instance is destroyed, then we obviously want to go
ahead and destroy the object associated with it as well.
So inside of this destroy function, as you can see, we're removing the object from our cache up here
where we store all of our objects.
And afterwards here we have to manually disconnect these events.
We have to destroy these other instances.
This is a lot of work.
And sometimes you may wish that there is an easier way to do this, because later on, if we decided
to add more instances or listen to some other connections, we may forget to disconnect those or destroy
them.
Down here in our destroy function.
And that can lead to issues like memory leaks.
So this is where trove comes in.
So I'm going to go ahead and require trove by first referring to the location that it's stored in.
I have it placed inside of replicated storage.
So I'll just call or create a variable for replicated storage.
And then I can go ahead and refer to my trove and we'll require from replicated storage trove.
And then we can go ahead and use this trove to create a new object or a new trove object.
And inside of that trove object is where we can store connections, we can store instances.
And whenever we decide to clean up, we can call the cleanup function on that trove.
And it goes ahead and deletes and disconnects all of those events and instances for us without us having
to manually do it within our destroy function.
So inside of my tool class, what I could do is I could create a new key for my trove.
I'm just going to name it underscore, underscore, trove to signify that this is private.
And I don't want any other scripts to really be messing with this key inside of my object.
So I'm just putting the two underlines to denote it as a private key.
And then I can set that equal to true revenue.
And now we have our object here where we could go ahead and add these instances inside of the object.
And we can also add these connections into our object.
And then when our part gets destroyed, we can easily just clean up all of those things by calling a
function on our trove object.
Now, before I completely start setting up this trove, I first want to demonstrate how this class actually
works in its default state.
So inside of this script, I've simply required my tool class.
I've created an example tool object here where I'm just passing a new tool instance.
And here's my new tool.
And then I'm going to wait 10s and then afterwards destroy my tool object.
So within those 10s we should have like for example, this heartbeat function running multiple times
per second and stuff like that.
So if we go and play test the game.
As you can see, our object there is executing with that heartbeat.
And if we look inside of our spawn location, here are those other instances associated with our object
that I created.
And then eventually, after those 10s are up, we destroy those objects.
We disconnect that function from that event, and it's no longer executing and our object has been destroyed.
But like I said earlier, that can be a little tedious since we have to manually do all of the work
right here inside of our destroy function.
So this is where our trove comes into play.
For example, here, when we create our new Bindable event, what we can do is we can store it inside
of our trove.
You can think of the trove as a collection of stuff inside of the script, or as a bin or a container.
And once you're done using the stuff inside of the trove, we go ahead and dump it out and trash it.
So to add a new object to my trove I'm going to do is first refer to it.
And then we have a function which is called add.
And this allows us to add an object to our trove such as this new Bindable event right here.
So I can call the add function and just pass self dot some event.
And now we have stored this Bindable event inside of the trove.
And that means whenever we clean up the trove this is automatically going to be destroyed for us.
We can also do all of this on the same line.
So for example here I could do self dot underscore underscore trove add.
And then inside of here I can call my instance dot new function.
And then this will also return back the new texture that we created and store it inside of this variable.
Both of these ways work.
But sometimes you may want to do it this way because it's one line instead of two lines.
But whatever way works for you doesn't really matter.
So now these two instances are inside of our trove, and that means now we don't have to manually destroy
those instances in our destroy function.
Instead, what we could do is refer to the trove.
So dot underscore underscore trove and then call the clean function.
And that will automatically destroy those instances for us.
So if we go take a look by running our game.
And we look in the workspace here, you can see those objects inside of our spawn location.
And then once those 10s are up.
As you can see, they have been automatically destroyed by our trove.
And of course, we can do the exact same thing with these connections as well with our events.
So inside of our trove, we also have a function to connect to a signal, pass a function, and then
when the trove is cleaned up it's going to automatically disconnect that function from that event.
So for example here I want to connect to the run service heartbeat event.
So we'll pass that and then pass our function.
And I'll just basically copy what happens here and paste it in there.
And then we can delete this.
And now we don't have to store that connection inside of our object, which means we don't have to manually
disconnect it.
So we can go ahead and delete that.
Because every time we call the clean function on our trove, that connection right here to our heartbeat
event is automatically going to be cleaned up.
And the same thing we could do for this right here as well.
Now another valid way you can add this connection to our trove is by using the add function instead
of the connect function.
So for example I could do self dot underscore underscore trove and then add and inside of this function
what I can do is just copy this and paste that in there and then delete that.
And this is the exact same thing as doing this up here.
Because remember when we connect to this event what returns is that connection that we can disconnect.
And that connection gets passed to the add function.
And then our trove is automatically going to clean up or disconnect that connection for us.
So now we don't have to manually disconnect that other connection right here either.
So we've just reduced four lines of code into a single line of code, where everything is stored inside
of our trove, and all we need to do is call the clean function.
And it's going to disconnect these events for us, and it's going to automatically delete these instances
for us as well.
So now if we go and play the game.
As you can see, our object is running.
We go look at our spawn location, there are our objects, and then eventually.
There we go.
Those objects get destroyed.
And as you can see, the event also got disconnected thanks to our trove.
So as you can see, trove is an incredibly simple module script, yet it is an extremely useful one
and can save us from the headache of memory leaks down the road in our games.
As I stated earlier, you can grab the trove module script attached to the lecture and take a look at
the documentation for it as well.
That's all for me for this lecture and I will see you in the next one.